#pragma once

#include <iostream>
#include <sstream>
#include <filesystem>
#include <fstream>
#include <mutex>
#include <memory>
#include <thread>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <cstdio>
#include <string>
#include <ctime>

namespace Local_Log
{
    // 日志默认路径和文件名
    const std::string defaultpath = "./log";
    const std::string defaultname = "log.txt";
    const std::string pathname = "./log/log.txt";

    // 日志等级
    enum struct LogLever
    {
        LDEBUG,
        LINFO,
        LWARNING,
        LERROR,
        LFATAL,
        HELLO,
        BYBY
    };

    // 日志等级转字符串
    std::string LogLeverToString(LogLever lever)
    {
        switch (lever)
        {
        case LogLever::LDEBUG:
            return "DEBUG";
        case LogLever::LINFO:
            return "INFO";
        case LogLever::LWARNING:
            return "WARNING";
        case LogLever::LERROR:
            return "ERROR";
        case LogLever::LFATAL:
            return "FATAL";
        case LogLever::HELLO:
            return "HELLO";
        case LogLever::BYBY:
            return "BYBY";
        }
        return "ERROR";
    }

    // 时间
    std::string GetCurrTime()
    {
        time_t nowtime = time(nullptr);
        struct tm curr;
        // linux修改位置1
        localtime_r(&nowtime, &curr);
        std::stringstream sstime;
        sstime << curr.tm_year + 1900 << '-'
               << curr.tm_mon << '-'
               << curr.tm_mday << ' '
               << curr.tm_hour << ':'
               << curr.tm_min << ':'
               << curr.tm_sec;
        return sstime.str();
    }

    // 日志刷新策略的接口
    // strategy 策略
    struct LogStrategy
    {
        // virtual 虚拟的
        virtual ~LogStrategy() = default;
        // sync 同步
        //  = 0; 表示纯虚函数，没有默认实现，由派生类重写
        // 包含纯虚函数的类为抽象类，抽象类不能实例化对象，只能由派生类继承
        virtual void SyncLog(const std::string &message) = 0;
    };

    // 显示器刷新类
    // console 控制台
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        virtual void SyncLog(const std::string &message)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            std::cout << message << std::endl;
        }
        ~ConsoleLogStrategy()
        {
            // std::cout << "ConsoleLogStrategy Destroy" << std::endl;
        }

    private:
        std::mutex _mutex;
    };

    // 文件刷新类
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(std::string path = defaultpath, std::string name = defaultname)
            : _logpath(path), _logname(name)
        {
            // int dir = mkdir(defaultpath.c_str(), 0755);
            // if (dir != -1)
            // {
            //     std::cout << "mkdir creat success" << std::endl;
            // }
            // else
            // {
            //     std::cout << "mkdir creat error" << std::endl;
            // }
            // int creatfile = open(pathname.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
            // if (creatfile != -1)
            // {
            //     std::cout << "open creat success" << std::endl;
            // }
            // else
            // {
            //     std::cout << "creat error" << std::endl;
            // }
            // close(creatfile);
            mkdir(defaultpath.c_str(), 0755);
        }
        virtual void SyncLog(const std::string &message)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            std::string pathname = _logpath + '/' + _logname;
            std::ofstream ofs(pathname.c_str(), std::ios_base::out | std::ios_base::app);
            if (!ofs.is_open())
            {
                std::cout << "Local_Log::FileLogStrategy::SyncLog::ofstream::open error" << std::endl;
                return;
            }
            ofs << message << std::endl;
            ofs.close();
        }
        ~FileLogStrategy()
        {
            // std::cout << "FileLogStrategy Destroy" << std::endl;
        }

    private:
        std::string _logpath;
        std::string _logname;
        std::mutex _mutex;
    };

    // 日志类
    class LocalLogger
    {
    public:
        LocalLogger()
        {
            UseConsoleLogStrategy();
        }
        // 刷新策略-文件刷新
        void UseFileLogStrategy()
        {
            _strategy = std::make_unique<FileLogStrategy>();
        }
        // 刷新策略-显示器刷新
        void UseConsoleLogStrategy()
        {
            _strategy = std::make_unique<ConsoleLogStrategy>();
        }

        // 生成日志的内部类
        class LogMessage
        {
        public:
            // RAII风格，构造的时候创建好日志头部
            LogMessage(LogLever lever, const std::string &filename, size_t line, LocalLogger &logger)
                : _nowtime(GetCurrTime()),
                  _lever(lever),
                  _id(std::this_thread::get_id()),
                  _filename(filename),
                  _line(line),
                  _logger(logger)
            {
                std::stringstream ss;
                ss << "[" << _nowtime << "] "
                   << "[" << LogLeverToString(_lever) << "] "
                   << "[" << _id << "] "
                   << "[" << _filename << "] "
                   << "[" << _line << "] -";
                _loghead = ss.str();
            }
            // RAII风格，析构刷新
            ~LogMessage()
            {
                if (_logger._strategy != nullptr)
                {
                    _logger._strategy->SyncLog(_loghead);
                }
            }
            // 重载 << 支持C++连续输出模式
            template <class T>
            LogMessage &operator<<(const T &t)
            {
                std::stringstream ss;
                // 将t通过流提取为字符串
                ss << t;
                _loghead += ss.str();
                // 支出连续的<<
                return *this;
            }

        private:
            std::string _nowtime;  // 日志时间
            LogLever _lever;       // 日志等级
            std::thread::id _id;   // 日志ID
            std::string _filename; // 日志文件
            size_t _line;          // 日志行号
            LocalLogger &_logger;  // 外部日志类
            std::string _loghead;  // 日志头部
        };
        // LogMessage(LogLever lever, std::string& filename, size_t line, LocalLogger& logger)
        //  故意拷⻉，形成LogMessage临时对象，后续在被<<时，会被持续引⽤，
        //  直到完成输⼊，才会⾃动析构临时LogMessage，⾄此也完成了⽇志的显⽰或者刷新
        //  同时，形成的临时对象内包含独⽴⽇志数据
        LogMessage operator()(LogLever lever, const std::string &filename, size_t line)
        {
            // 返回匿名对象
            return LogMessage(lever, filename, line, *this);
        }

    private:
        std::unique_ptr<LogStrategy> _strategy; // 日志写入策略
    };

    // 定义全局的logger对象
    LocalLogger logger;
    // 使⽤宏，可以进⾏代码插⼊，⽅便随时获取⽂件名和⾏号
// #define LOG(type) logger(type, __FILE__, __LINE__)
#define LOG(type) logger(type, __FILE__, __LINE__)
// 提供选择使⽤何种⽇志策略的⽅法
#define ENABLE_CONSOLE_LOG_STRATEGY() logger.UseConsoleLogStrategy()
#define ENABLE_FILE_LOG_STRATEGY() logger.UseFileStrategy()
}